GithubActions Extension VSCode By Flutter

こんにちはEveryDaySoft代表の永田です。

今回はFlutterでGithub Actionsと VScodeのExtension紹介

GitHub Actionsの進捗を確認できる機能です。

VSCodeエディター

iconマークをタップすると、githubにweb遷移します。

GitHubActionsで、FastlaneとDeploygate配信、AppStore配信、GooglePlayConsole配信の紹介をいたします。

FastlaneとDeploygateはローカル上のコマンドでも動作するので、何らかの理由でCIが動かない場合も対応できるので、良いと思いました。

前提

App Store Connect、Google Play Consoleで配信するアプリがあること

iOSで使用する証明書がないこと、ある場合は削除する必要があります。

 

 

Gemfile作成

iOSとAndroidのディレクトリー
bundle init
bundle install
iOSとAndroidのディレクトリー
Gemfileのファイル作成
frozen_string_literal: true
source "https://rubygems.org"
gem "fastlane"
iOSとAndroidのデレクトリーで
fastlane init
iOSの場合
2. 👩‍✈️  Automate beta distribution to TestFlight
Please enter your Apple ID developer credentials
Apple ID Username: Apple IDを入力

Continue by pressing Enter ⏎ Enter を数回繰り返して問題ありません。

fastlaneのディレクトリーが作成されます。

AppfileとFastfileが作成されます。*androidも同様です。

Appfile作成

app_identifier("自動生成") # The bundle identifier of your app
apple_id("自動生成") # Your Apple Developer Portal username

itc_team_id("自動生成") # App Store Connect Team ID
team_id("自動生成") # Developer Portal Team ID

# For more information about the Appfile, see:
# https://docs.fastlane.tools/advanced/#appfile

Matchfile作成

fastlaneのディレクトリーで
fastlane match init
証明書を自動で作成して、連動する目的です。

URL of the Git Repo: GitHubなどのurlを設定願います。

MatchFileが作成されます。*androidも同様です。

解説は自動で英語で記載されてます。

developの場合、証明書が作成されます。

fastlane match development 

development <-ここをMatchFileのTypeに応じて記載します。

作成以外で使用する場合–readonlyを付与します。

fastlane match development --readonly

成功すると、このようになります。

証明書が既にあり、削除して再作成する必要がある場合は

fastlane match nuke development

 

 

iOS_Fastfile作成

App Store Connectのキーを作成画面
fastlane/.envファイル内で作成
api_key

ここから
ASC_KEY_ID="キーID"
ASC_ISSUER_ID="Issuer ID"
ASC_KEY_CONTENT="一度だけダウンロードできる.jsonファイル
ここまで
base64のコマンドを使用して生成
base64 --i pc-api- ~~.json| tr -d  "\n" | pbcopy
fastlane/.envファイル内で作成
create_keychain

ここから
MATCH_KEYCHAIN_NAME="キーチェーン名"
MATCH_KEYCHAIN_PASSWORD="pcで使用しているキーチェーンアクセスを解除するパスワード"
ここまで
fastlane/.envファイル内で作成
USERNAME="fastlaneで使用しているメールアドレス"
PERSONAL_GITHUB_ACCESS_TOKEN="個人用アークセストークン"
app_identifierはXcodeのBundleidntifier
default_platform(:ios)

platform :ios do
  desc "Push a new release build to the App Store"
  lane :release do
    
    api_key = app_store_connect_api_key(
      key_id: ENV['ASC_KEY_ID'], 
      issuer_id: ENV['ASC_ISSUER_ID'],
      key_content: ENV['ASC_KEY_CONTENT'],
      is_key_content_base64: true,
    )
    # https://docs.fastlane.tools/actions/create_keychain/
    # キーチェーンがないと出力されますが、配信は成功します。
    # これがないとfastlaneのupload_to_app_storeが成功しない。
    create_keychain(
      name: ENV['MATCH_KEYCHAIN_NAME'],
      password: ENV['MATCH_KEYCHAIN_PASSWORD'],
      default_keychain: true,
      unlock: true,
      timeout: 3600,
      lock_when_sleeps: true
    )
 
    username = ENV['USERNAME']
    personal_github_access_token = Base64.strict_encode64(ENV['PERSONAL_GITHUB_ACCESS_TOKEN'])
    authorization_token_str = "#{username}:#{personal_github_access_token}"
    basic_authorization_token = Base64.strict_encode64(authorization_token_str)

    # https://docs.fastlane.tools/actions/match/
    match(
      git_basic_authorization:basic_authorization_token,
      api_key: api_key, 
      app_identifier: ["XcodeのBundleidntifier"],
      readonly: false,
      type: "appstore", #addhocなどもあります
      readonly: is_ci
    )
    # https://docs.fastlane.tools/actions/build_app/
    build_app(
      workspace: "Runner.xcworkspace",
      scheme: "Runner",
      export_options: {
        method: "app-store",
      },
    )
    # https://docs.fastlane.tools/actions/upload_to_app_store/
    upload_to_app_store(
      api_key: api_key,
      skip_metadata: true,
      skip_screenshots: true,
      precheck_include_in_app_purchases: false, 
    )
  end
end

Android_Fastfile作成

app-release.apkとapp-release.aabは事前に作成します。

androidディレクトリーでコマンド実施

./gradlew clean && ./gradlew :app:bundleRelease  <-release.apkの作成
flutter build appbundle  --no-tree-shake-icons --build-name=1.0.0 --build-number=1. <-aabの作成
release_status: 'draft', <-リリース前、内部公開のためdraft
default_platform(:android)

platform :android do
  before_all do |lane, options|
    ENV["DEPLOYGATE_API_TOKEN"] = 'DEPLOYGATEに記載してます。'
  end

  desc "Submit a new Beta Build to Crashlytics Beta"
  lane :release do
    sh('cd ../../ && flutter build apk --release')
    deploygate(
      api_token: ENV["DEPLOYGATE_API_TOKEN"],
      user: 'DEPLOYGATEのユーザー名',
      message: "DeployGate Test配信",
      apk: "../build/app/outputs/apk/release/app-release.apk",
    )
 
    upload_to_play_store(
      track: 'internal',
      release_status: 'draft',
      aab: "../build/app/outputs/bundle/release/app-release.aab",
      skip_upload_metadata: true,
      skip_upload_images: true,
      skip_upload_screenshots: true
    )
  end
 end

yml作成

Flutterの 7.7.7+0 の 0の数値をGitHubActionsを実行する際は上げてください。

GitHub ActionsのNodeを最新バージョンを設定しています。

iOSのApp Store Connectのビルドは10数分で配信メールが届きます。

      - uses: actions/checkout@v3
        with:
          fetch-depth: 1
      - name: Use Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '16'

FlutterのTest解析を行うスクリプト

  flutter_analyze:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 1
      - name: Use Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '16'
      - uses: subosito/flutter-action@v2
        with:
          channel: 'stable'
      - run: flutter pub get
      - run: flutter analyze
      - run: flutter test

Flutter、iOSのAppStoreConnect配信スクリプト

secretsの箇所は、GitHub で設定します。

base64のコマンドで作成するもの -> は解説です。
ここから
PROVISIONING_PROFILE -> ダウンロードしたAppleのprofile
CERTIFICATES_P12 -> キーチェーンアクセスにある使用中の証明書から作成したp12ファイル
ASC_API_KEY_CONNECT -> AppStoreConnectユーザとアクセスのキーから一度だけダウンロード可能なjsonファイル
SSH_PRIVATE_KEY -> id_ed25519の公開キーをbase64コマンドでコピー
base64 --i pc-api- id_ed25519| tr -d  "\n" | pbcopy
ここまで

base64のコマンドで作成しないもの -> は解説です。
ここから
CERTIFICATE_PASSWORD -> 作成したp12ファイルに設定したパスワード
TEAM_ID -> キーチェーン名の別名(1111111111)
MATCH_KEYCHAIN_NAME -> キーチェーン名
MATCH_KEYCHAIN_PASSWORD -> キーチェーンロック解除するパスワード
ASC_API_KEY_ID -> キーID
ASC_API_ISSUER_ID -> Issuer ID
ここまで

GitHubActionsからはssh keyの認証ができないので、

ローカルの.sshディレクトリーで作成

$ ssh-keygen -t ed25519

全て質問はエンター

pbcopy < ~/.ssh/id_ed25519 公開キーをコピー

https://github.com/settings/keys 公開キーの登録

秘密キーはgithubのsecrets.箇所で登録
  release-ios:
    name: Build and release iOS app
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '16'
      - uses: maxim-lobanov/setup-xcode@v1
        with:
          xcode-version: latest-stable
      - uses: subosito/flutter-action@v2.8.0
        with:
          flutter-version: '3.7.1'
      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.2.0'
      - run: |
          cd ios
          bundle install
      - name: Install packages
        run: flutter pub get
      - name: Import Provisioning Profile
        env:
          PROVISIONING_PROFILE: ${{ secrets.PROVISIONING_PROFILE }}
        run: |
          mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
          touch ~/Library/MobileDevice/Provisioning\ Profiles/decoded.mobileprovision
          echo -n $PROVISIONING_PROFILE | base64 -d -o ~/Library/MobileDevice/Provisioning\ Profiles/decoded.mobileprovision
      - name: Import Code-Signing Certificates
        uses: apple-actions/import-codesign-certs@v2
        with:
          p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
          p12-password: ${{ secrets.CERTIFICATE_PASSWORD }}
      # fastlaneで必要なため、SSHキーのセットアップをする.
      - name: Setup SSH Keys and known_hosts for fastlane match
        run: |
          SSH_PATH="$HOME/.ssh"
          mkdir -p "$SSH_PATH"
          touch "$SSH_PATH/known_hosts"
          echo "$PRIVATE_KEY" > "$SSH_PATH/id_ed25519"
          chmod 700 "$SSH_PATH"
          ssh-keyscan github.com >> ~/.ssh/known_hosts
          chmod 600 "$SSH_PATH/known_hosts"
          chmod 600 "$SSH_PATH/id_ed25519"
          eval $(ssh-agent)
          ssh-add "$SSH_PATH/id_ed25519"
        env:
          PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
      - name: Build & upload iOS binary
        run: |
          cd ios
          ls
          bundle exec fastlane release
        env:
          TEAM_ID: ${{ secrets.TEAM_ID }}
          MATCH_KEYCHAIN_NAME: ${{ secrets.MATCH_KEYCHAIN_NAME }}
          MATCH_KEYCHAIN_PASSWORD: ${{ secrets.MATCH_KEYCHAIN_PASSWORD }}
          MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }}
          ASC_API_KEY_ID: ${{ secrets.ASC_API_KEY_ID }}
          ASC_API_ISSUER_ID: ${{ secrets.ASC_API_ISSUER_ID }}
          ASC_API_KEY_CONNECT: ${{ secrets.ASC_API_KEY_CONNECT }}

AndroidでGoogle Play ConsoleとDeploygate配信のスクリプト

リリース.apkの作成 android/lib/build.gradle

keytool -genkey -v -keystore keystore名 -alias keyAlias名 -keyalg RSA -keysize 2048 -validity 36500 <-36500は有効期間
キーストアのパスワードを入力してください: keyPassword/storePassword
新規パスワードを再入力してください: keyPassword/storePassword
姓名は何ですか。
  [Unknown]:  名前
組織単位名は何ですか。
  [Unknown]:  組織名
組織名は何ですか。
  [Unknown]:  地域名
都市名または地域名は何ですか。
  [Unknown]:  Musashino-shi
都道府県名または州名は何ですか。
  [Unknown]:  Tokyo
この単位に該当する2文字の国コードは何ですか。
  [Unknown]:  JP
CN=Wataru Yamada, OU=app team, O=org yamacraft, L=Musashino-shi, ST=Tokyo, C=JPでよろしいですか。
  [いいえ]:  y
    signingConfigs {
        release {
            storeFile file('./keystore名')
            storePassword 'パスワード'
            keyAlias 'keystorenameファイルを作成時のkeyAlias名'
            keyPassword 'パスワード'
        }
    }

    buildTypes {
        release {
            // TODO: Add your own signing config for the release build.
            // Signing with the debug keys for now, so `flutter run --release` works.
            signingConfig signingConfigs.release
        }
    }

 

Deploygateに必要な配信設定

GitHub secretsの設定と、Androidディレクトリー内の/fastlane/.envに作成

https://docs.deploygate.com/ja/docs/developer-tools/fastlane/

JSON_KEY_PATHはGCPで作成したJsonキーをbase64化しgithub secrets.登録

公式

Google Cloud Platformで表示

操作セクションからリストを表示

ダウンロードしたjsonはbase64化して、github secrets.登録

  release-android:
    name: Build and release Android app
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '16'
      - uses: subosito/flutter-action@v2.8.0
        with:
          flutter-version: '3.7.1'
      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.2.0'

      - name: ExportPath
        run: |
          export PATH=$PATH:${FLUTTER_HOME}/bin/cache/dart-sdk/bin
          export PATH=$PATH:${FLUTTER_HOME}/.pub-cache/bin
      - name: Building Android AppBundle
        run: flutter build appbundle --build-number ${GITHUB_RUN_NUMBER} # build-number
      - name: Run builds(fastlane)
        run: |
          cd android
          fastlane deploy
        env:
          JSON_KEY_PATH: ${{ secrets.JSON_KEY_PATH }}

全体のコード、branchesはプルリクエストをした場合にGitHubActionsが実行されます。

name: GithubActions extension VSCODE
on:
  push:
    branches: [ test ]

  workflow_dispatch:

jobs:
  flutter_analyze:
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 1
      - name: Use Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '16'
      - uses: subosito/flutter-action@v2
        with:
          channel: 'stable'
      - run: flutter pub get
      - run: flutter analyze
      - run: flutter test
  release-ios:
    name: Build and release iOS app
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '16'
      - uses: maxim-lobanov/setup-xcode@v1
        with:
          xcode-version: latest-stable
      - uses: subosito/flutter-action@v2.8.0
        with:
          flutter-version: '3.7.1'
      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.2.0'
      - run: |
          cd ios
          bundle install
      - name: Install packages
        run: flutter pub get
      - name: Import Provisioning Profile
        env:
          PROVISIONING_PROFILE: ${{ secrets.PROVISIONING_PROFILE }}
        run: |
          mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
          touch ~/Library/MobileDevice/Provisioning\ Profiles/decoded.mobileprovision
          echo -n $PROVISIONING_PROFILE | base64 -d -o ~/Library/MobileDevice/Provisioning\ Profiles/decoded.mobileprovision
      - name: Import Code-Signing Certificates
        uses: apple-actions/import-codesign-certs@v2
        with:
          p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
          p12-password: ${{ secrets.CERTIFICATE_PASSWORD }}
      # fastlaneで必要なため、SSHキーのセットアップをする.
      - name: Setup SSH Keys and known_hosts for fastlane match
        # https://github.com/maddox/actions/blob/master/ssh/entrypoint.sh
        run: |
          SSH_PATH="$HOME/.ssh"
          mkdir -p "$SSH_PATH"
          touch "$SSH_PATH/known_hosts"
          echo "$PRIVATE_KEY" > "$SSH_PATH/id_ed25519"
          chmod 700 "$SSH_PATH"
          ssh-keyscan github.com >> ~/.ssh/known_hosts
          chmod 600 "$SSH_PATH/known_hosts"
          chmod 600 "$SSH_PATH/id_ed25519"
          eval $(ssh-agent)
          ssh-add "$SSH_PATH/id_ed25519"
        env:
          PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY2 }}
      - name: Build & upload iOS binary
        run: |
          cd ios
          ls
          bundle exec fastlane release
        env:
          TEAM_ID: ${{ secrets.TEAM_ID }}
          MATCH_KEYCHAIN_NAME: ${{ secrets.MATCH_KEYCHAIN_NAME }}
          MATCH_KEYCHAIN_PASSWORD: ${{ secrets.MATCH_KEYCHAIN_PASSWORD }}
          MATCH_GIT_BASIC_AUTHORIZATION: ${{ secrets.MATCH_GIT_BASIC_AUTHORIZATION }}
          ASC_API_KEY_ID: ${{ secrets.ASC_API_KEY_ID }}
          ASC_API_ISSUER_ID: ${{ secrets.ASC_API_ISSUER_ID }}
          ASC_API_KEY_CONNECT: ${{ secrets.ASC_API_KEY_CONNECT }}
  release-android:
    name: Build and release Android app
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '16'
      - uses: subosito/flutter-action@v2.8.0
        with:
          flutter-version: '3.7.1'
      - uses: ruby/setup-ruby@v1
        with:
          ruby-version: '3.2.0'

      - name: ExportPath
        run: |
          export PATH=$PATH:${FLUTTER_HOME}/bin/cache/dart-sdk/bin
          export PATH=$PATH:${FLUTTER_HOME}/.pub-cache/bin
      - name: Building Android AppBundle
        run: flutter build appbundle --build-number ${GITHUB_RUN_NUMBER} # build-number
      - name: Run builds(fastlane)
        run: |
          cd android
          fastlane deploy
        env:
          JSON_KEY_PATH: ${{ secrets.JSON_KEY_PATH }}

ローカル対応

iOSディレクトリで

bundle exec fastlane release

Androidはapkとaabをコマンドで事前に作成

androidディレクトリで

bundle exec fastlane deploy

iOSのAppStoreConnectのビルド配信

iOSのAppStoreConnectのTestFlightへのビルド配信

Android,Google Play Consoleの内部配信(リリース済みの場合 製品版も可能)

Android、Deploygate配信(リリースAPK)