🙌

AndroidでGithub Actionsを活用する

に公開

自動ワークフロー

.github/workflowsに作っていく

プルリクエスト時

ビルドする

# ワークフローの名前(GitHub のActionsタブに表示される)
name: PR Checks
# イベントのトリガー
on:
  pull_request:
jobs:
  # ジョブ名
  build-and-test:
    # ジョブが実行される環境
    runs-on: ubuntu-latest
    steps:
      # ソースコードのチェックアウト(ダウンロード)
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up JDK
        uses: actions/setup-java@v4
        with:
          java-version: '17'  # Javaのバージョンを指定
          distribution: 'temurin'  # JDKのディストリビューションを指定

      # 実行権限を付与するステップを追加
      - name: Grant execute permission for gradlew
        run: chmod +x gradlew
      
      # アプリビルドの実行
      - name: Build with Gradle
        run: ./gradlew assembleDebug

Lintチェックする

      # Lintチェックの実行
      - name: Run Lint
        run: ./gradlew lintDebug

単体テストする

      # 単体テスト実行
      - name: Run Unit Tests
        run: ./gradlew testDebugUnitTest

タグを付与する

  # PRにラベルを付けるジョブ
  label-pr:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
      issues: write
    steps:
      - name: Checkout code
        uses: actions/checkout@v4
      
      - name: Add PR labels based on changes files
      # labelerアクションを使用する、labeler.ymlファイルを参照
        uses: actions/labeler@v5
        # 書き込みにはGitHubトークンが必要(ワークフロー内のみ有効)
        with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}
          sync-labels: true   # ラベルの自動作成を有効化

jobが複数ある場合依存関係がない場合デフォルトで並列実行になる
ちなみにタイムアウトの時間はデフォルトで360分なので明示的に指定してもいいかもしれない

# ワークフローの名前(GitHub のActionsタブに表示される)
name: PR Checks
# イベントのトリガー
on:
  pull_request:
jobs:
  # ジョブ名
  build-and-test:
    # ジョブが実行される環境
    runs-on: ubuntu-latest
    # 3タイムアウトを30分に指定
    timeout-minutes: 30
    steps:
      # ソースコードのチェックアウト(ダウンロード)
      - name: Checkout code
        uses: actions/checkout@v4

.github/labeler.yamlを作成する
pathとファイル名はこれじゃないと動かないので注意

UI:
  - changed-files:
      - any-glob-to-any-file: ['**/ui/**']

Domain:
  - changed-files:
      - any-glob-to-any-file: ['**/domain/**']

Data:
  - changed-files:
      - any-glob-to-any-file: ['**/data/**']

Test:
  - changed-files:
      - any-glob-to-any-file: ['**/test/**', '**/androidTest/**']

Config:
  - changed-files:
      - any-glob-to-any-file: ['**/*.gradle', '**/*.gradle.kts', '**/*.toml', '**/gradle.properties', '**/AndroidManifest.xml']

手動ワークフロー

リリース作業を自動化する

実現すること

  • Githubでアップデートするversion指定する
  • gradleファイルのversionをアップデートしたPRを出す
  • リリースノートをドラフトで作成、前回との差分を表示、署名済みのapkとaabを添付する

これらを行う必要があるがゼロから行うと結構大変なので既に公開されているactionを利用する
実現したい内容がすでに公開されているactionにあるか確認したほうがいい
公式
https://github.com/actions
その他
https://github.com/marketplace?type=actions

行うこと

  • ローカルでキーストアを作成する
  • Githubでの秘匿情報の設定
  • build.gradle.ktsの修正
  • Githubでのワークフロー時のPermission設定をする
  • ワークフローの作成

ローカルでキーストアを作成する

Android Studioのツールバーからbuild > Generated Signed App Bundle or Apkから作成

Githubでの秘匿情報の設定

Github Actionsで署名付きアプリを作るにあたって必要になる。秘匿情報は必要だがリポジトリに含めるわけにはいかないため。
Githubでリポジトリに移動してからSettings > Seacrets and Valiables > actions
Repository secretsに

  • KEYSTORE_BASE64
  • KEYSTORE_PASSWORD
  • KEY_ALIAS
  • KEY_PASSWORD
    を設定していく
    KEYSTORE_PASSWORD、KEY_ALIAS、KEY_PASSWORDはローカルで設定したものをそのまま設定する
    KEYSTORE_BASE64はひと手間必要になってくる
    ローカルで作成したキーストア(~.jks)を設定したいがバイナリファイルためBase64にエンコードしたものをGithubに設定する
    bashで

base64 -w 0 sampleActions.jks > keystore.base64

keystore.base64の内容をKEYSTORE_BASE64に設定する

build.gradle.ktsの修正

ローカルでビルドする場合はlocal.propertiesの設定を使用する
ワークフローではlocal.propertiesがないのでそれを利用して分岐する
Githubの秘匿情報から取得するするように設定する

signingConfigs {
        create("release") {
            val localProps = rootProject.file("local.properties")
            if (localProps.exists()) {
                val props = Properties().apply { load(localProps.inputStream()) }
                storeFile = rootProject.file(props["storeFile"] ?: "")
                storePassword = props["storePassword"] as? String ?: ""
                keyAlias = props["keyAlias"] as? String ?: ""
                keyPassword = props["keyPassword"] as? String ?: ""
            } else {
                storeFile = file("release.jks")
                storePassword = System.getenv("KEYSTORE_PASSWORD")
                keyAlias = System.getenv("KEY_ALIAS")
                keyPassword = System.getenv("KEY_PASSWORD")
            }
            
        }
    }

    buildTypes {
        release {
            signingConfig = signingConfigs.getByName("release")
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }

Githubでのワークフロー時のPermission設定をする

Githubのリポジトリに移動してからSettings > actions > General
Workflow permissionsで
Read and write permissions
Allow GitHub Actions to create and approve pull requests
書き込みとPRを作成する権限を与える

ワークフローの作成

.github/workflowsにrelease.ymlを作成

name: Android Release

on:
  workflow_dispatch:
    inputs:
      version_name:
        description: 'Version Name (例: 1.0.1)'
        required: true
        type: string

permissions:
  contents: write
  pull-requests: write

jobs:
  release:
    runs-on: ubuntu-latest
    env:
      GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

    steps:
      - uses: actions/checkout@v4
        with:
          token: ${{ secrets.GITHUB_TOKEN }}

      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
          cache: gradle

      - name: Grant execute permission for gradlew
        run: chmod +x gradlew

      - name: Decode keystore
        run: echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > app/release.jks

      - name: Build signed APK/AAB
        env:
          KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
          KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
          KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
        run: |
          ./gradlew assembleRelease
          ./gradlew bundleRelease

      - name: Clean up keystore
        if: always()
        run: rm -f app/release.jks

      - name: Create GitHub Release
        uses: ncipollo/release-action@v1
        with:
          tag: "v${{ github.event.inputs.version_name }}"
          name: "Release v${{ github.event.inputs.version_name }}"
          # 自動生成リリースノートを有効化
          generateReleaseNotes: true
          artifacts: |
            app/build/outputs/apk/release/*.apk
            app/build/outputs/bundle/release/*.aab
          draft: true
          token: ${{ secrets.GITHUB_TOKEN }}

      - name: Bump Version
        uses: chkfung/android-version-actions@v1.2.2
        with:
          gradlePath: app/build.gradle.kts
          versionName: ${{ github.event.inputs.version_name }}
          versionCode: ${{ github.run_number }}

      - name: Create Pull Request
        uses: peter-evans/create-pull-request@v6
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          commit-message: "Release v${{ github.event.inputs.version_name }} (build ${{ github.run_number }})"
          branch: "release/v${{ github.event.inputs.version_name }}-${{ github.run_number }}"
          title: "Release v${{ github.event.inputs.version_name }}"
          body: |
            ## Release Summary
            Version: ${{ github.event.inputs.version_name }}
            Build: ${{ github.run_number }}
            
            ## Files Changed
            - Updated version in `app/build.gradle.kts`
            
            ## Release Assets
            - APK: Available in GitHub Release
            - AAB: Available in GitHub Release
            
            ## Next Steps
            1. Review and test the release
            2. Merge this PR to main
            3. Publish the GitHub Release (currently draft)
          draft: false
          delete-branch: true

ワークフローの流れ

  • 環境準備
    • リポジトリチェックアウト
    • JDK 17セットアップ
    • gradlewに実行権限付与
  • アプリビルド
    • キーストア作成
    • 署名済みAPK/AAB生成
    • キーストア削除(セキュリティ)
  • リリース作成
    • GitHub Releaseを作成(ドラフト)
  • バージョン更新
    • build.gradle.ktsのバージョン番号更新
    • Pull Request作成

詳細

  • workflow_dispatchとすることで手動のワークフローになる
  • inputsで何を入力するか定義する
  • permissionsで書き込み権限を付与する
  • Set up JDK 17ではgradleを使用できるようにする
  • Grant execute permission for gradlewではgradlewが使用できるように実行権限を付与する
  • Decode keystoreではbase64の形式で設定している秘匿情報をデコードする
  • Build signed APK/AABではenvでGithubの秘匿情報の設定を読み込む
    • ワークフローのenv → GradleのSystem.getenv() → 署名設定完成
  • Clean up keystoreではワークフローが失敗しても必ずキーストアを削除する
    • 後でPR作るときに含まれてしまう
  • Create GitHub Releaseでは公開されているActionを利用する、artifactsを設定すると指定したものをリリースノートに添付できる
  • Bump Versionでは公開されているActionを利用する
  • Create Pull Requestは公開されているActionを利用する

Discussion