Android

社内ホストしている GHES の Github Packages を Maven リポジトリとして利用して Android ライブラリを社内に公開する

Android

minne 事業部で Android アプリエンジニアをやっている tick-taku です。

この投稿では GitHub Enterprise Server (以下 GHES) の GitHub Packages を Maven リポジトリとして利用し、Gradle で Android アプリに導入するまでを公開します。

概要

Android でライブラリを利用しようと思うと Gradle で mavenCentral などからインポートすることになりますが、「ライブラリを社内を横断して配布したい、でも外部公開したくない」と言ったケースがありました。そのため、社内で利用している GHES の GitHub Packages を利用してクローズドな環境にライブラリをホストして、Gradle でインポートすることにしました。

提供するライブラリ側の設定と Android プロジェクトで利用するまでを解説していきます。

アップロードする Android ライブラリの準備

まずは GitHub Packages にホストする Android のライブラリを作成しましょう。Android プロジェクトを作成し、New Module から Android Library module を作成します。

今回は以下のようにサービスごとにインポートする module を用意する構成としました。interface などを core module に定義し提供する口はそろえ、core module の依存を伝搬するように内部実装はサービスに合わせた module を作成します。sample module は API の使用例を載せています。

.
├── buildSrc
├── core
├── minne
├── sample
└── suzuri

最後に作成したプロジェクトをリポジトリに push しましょう。

GitHub Packages へのアップロード

maven publish plugin のセットアップ

準備で作成したライブラリを GitHub Packages へアップロードするため、プラグインを導入します。今回は gradle-maven-publish-plugin を利用してアップロードします。pom.xml に必要な情報を gradle.properties にまとめられ管理しやすい点を評価しています。

まずは ルートの build.gradle に classpath を追加します。

buildscript {
    dependencies {
        classpath "com.vanniktech:gradle-maven-publish-plugin:0.18.0"
    }
}

次にアップロードする module の build.gradle にプラグインのセットアップをします。

plugins {
    id 'com.vanniktech.maven.publish'
}

android {
    ...
}

dependencies {
    api project(':core')
}

mavenPublish {
    releaseSigningEnabled = false
    sonatypeHost = null
}

最後に gradle.propeties を用意して pom.xml の情報を記載します。

VERSION_NAME=0.0.1
GROUP=pepabo.mobile.hoge
POM_PACKAGING=aar

また各 module 内に artifactId をわける必要があるため gradle.propeties を用意します。今回 VERSION_NAME をルートの gradle.properties に指定していますが、こちらに指定すると各 module ごとに version が管理できます。

POM_ARTIFACT_ID=minne

maven の認証設定

Maven リポジトリとして GitHub Packages を利用するにあたり、maven の向き先と認証情報を定義する必要があります。

publishing {
    repositories {
        maven {
            name = "GitHubPackages"
            url = uri("https://maven.<GHES のドメイン>/<organization>/<Repository Name>")
            credentials {
                username = "<GHES のユーザ名>"
                password = "<Parsonal Access Token>"
            }
        }
    }
}

nameGitHubPackages 固定です。url にはリポジトリの GitHub Packages の URL を指定します。GitHub Packages の URL のドメインの最初に maven. をつけたものになると思います。

credentials に GHES の認証情報を記載します。認証には Parsonal Access Token (以下 PAT)が必要になるので準備しておきましょう。scope は repowrite:packagesread:packages を指定して発行します。username には GHES のユーザ名、password には発行した PAT を指定します。

おそらく本番のプロダクトでは GitHub Actions など CI/CD でビルドしたりしていると思います。ペパボでも GitHub Actions を利用しているので Secrets にそれぞれを登録しておき Actions で環境変数にセットして呼び出すようにしています。また、 ローカル環境でもビルドしたいので以下のように環境変数を設定しておきます。

credentials {
    username = System.getenv("PACKAGES_ACCESS_NAME")
    password = System.getenv("PACKAGES_ACCESS_TOKEN")
}
export PACKAGES_ACCESS_NAME=<GHES のユーザ名>
export PACKAGES_ACCESS_TOKEN=<Parsonal Access Token>

ここで個人のトークンを利用していると、例えばその方が退職したりしてユーザが存在しなくなった場合 CI でのビルドが失敗します。それは困るので、ペパボでは pepabot と言う bot ユーザが存在していてそのアカウントのアクセストークンを Secrets に登録し利用しています。

アップロードの確認

以上で公開の設定は完了なので実際にアップロードしてみます。プロジェクトのルートディレクトリで以下のコマンドを実行します。

./gradlew :minne:publish --no-daemon --no-parallel

success と出たらアップロード完了です。Repository の GitHub Packages を確認するとアップロードした module が登録されているはずです。

プロジェクトへの導入

アップロードと同様の認証情報をルートの build.gradle に設定します。また、ここで必要となる PAT の scope は read:packages なので発行しておきましょう。ペパボではこちらもアップロードと同様に PAT を環境変数と Secrets にセットして運用しています。

allprojects { 
    repositories {
        maven {
            name = "GitHubPackages"
            url = uri("https://maven.<GHES のドメイン>/<organization>/<Repository Name>")
            credentials {
                username = "<GHES のユーザ名>"
                password = "<Parsonal Access Token>"
            }
        }
    }
}

最後に使用する module の build.gradle に依存関係を追加して Sync Project を実行するとライブラリが利用できるようになります。dependencies にアップロードしたライブラリを追加しましょう。groupId や artifactId は maven publish plugin のセットアップで用意した gradle.properties の GROUPPOM_ARTIFACT_ID になります。

dependencies {
    implementation '<groupId>:<artifactId>:<version>'
}

以上で、GHES の GitHub Packages にアップロードしたライブラリをプロジェクトで利用する方法でした。

GitHub Actions を利用したライブラリの自動アップロード

最後にタグを push したときにライブラリを自動でアップロードする Actions を紹介します。

当然毎回ライブラリを手動でリリースするのも大変なので GitHub Actions を利用して何らかのトリガーで自動でアップロードしたくなります。x.y.z の形式のタグが push されたら gradle.propaties の VERSION_NAME をリプレイスしてコミットし、リリースコマンドを実行します。あとは例えば master にマージされるとタグを push する Actions と併用すると PR が master にマージされるとインクリメントしてリリースすることが可能です。

name: Deploy to GitHub Packages

on:
  push:
    tags:
      - '[0-9]*.[0-9]*.[0-9]*'

jobs:
  build_and_deploy:
    runs-on: normal
    container:
      image: <docker image>
    steps:
    - uses: actions/checkout@v2
      with:
        ref: master
    - name: Grant execute permission for gradlew
      run: chmod +x gradlew
    - name: Update version
      run: |
        sed -i "s/VERSION_NAME=[0-9]*.[0-9]*.[0-9]*/VERSION_NAME=${GITHUB_REF#refs/tags/}/g" ./gradle.properties
        rm -rf gradle.propreties-e
        git config user.name "<user name>"
        git config user.email "<メールアドレス>"
        git add .
        git commit -m "Update version ${GITHUB_REF#refs/tags/}"
        git push
    - name: Clean build
      run: ./gradlew clean build
    - name: Deploy
      run: |
        ./gradlew :core:publish --no-daemon --no-parallel
        ./gradlew :minne:publish --no-daemon --no-parallel
        ./gradlew :suzuri:publish --no-daemon --no-parallel
      env:
        PACKAGES_ACCESS_NAME: ${{ secrets.PACKAGES_ACCESS_NAME }}
        PACKAGES_ACCESS_TOKEN: ${{ secrets.PACKAGES_ACCESS_TOKEN }}

まとめ

以上が Android のライブラリを社内ホストしている GHES の GitHub Packages にアップロードして Android プロジェクトで利用するまでの流れになります。

これにより、例えば暗号化処理でアルゴリズムは社内全体で共通化する方針となったときなどにクローズドにしておきたいライブラリを事業部を横断して利用できるようになるため各リポジトリ内に分散していたコードが一元管理でき実装コストも下がりますし、プロジェクトで閉じていたコードが事業部外のメンバーからのフィードバックを得やすくなりよりパフォーマンスを高めることも期待できます。

その他ペパボでは RubyGem レジストリとしても利用 しています。また、今は GitHub Packages が対応していませんが Swift や Dart が対応されれば iOS や Flutter でも同様にライブラリをホストしていきたいと考えています。

参考