Help us understand the problem. What is going on with this article?

Android LibraryをGitHub ActionsでビルドしGitHub Packagesで公開する

この記事はZOZOテクノロジーズ #1 Advent Calendar 2019 1日目の記事になります。

また、今年は全部で5つのAdvent Calendarが公開されています。

概要

先月のGitHub UniverseでGitHub ActionsとGitHub Packages(旧GitHub Package Registry)が正式リリースされました。GitHub PackagesはGitHubと統合されたパッケージのホスティングサービスで、ソースコードとその成果物であるパッケージを一括で管理できます。

integrated permissions management and billing

とあるので、package単位での課金がサポートされるかもしれませんね。

Supported clients and formatsを見るとGitHub Packagesは以下のクライアントとファイルフォーマットをサポートします。

Package client Language Package format Description
npm JavaScript package.json Node package manager
gem Ruby Gemfile RubyGems package manager
mvn Java pom.xml Apache Maven project management and comprehension tool
gradle Java build.gradle or build.gradle.kts Gradle build automation tool for Java
docker N/A Dockerfile Docker container management platform
nuget .NET nupkg NuGet package management for .NET

Apache Mavenのpom.xmlフォーマットに対応しており、また、クライアントとしてGradleをサポートしています。したがって、GitHub PackagesにAndroid Libraryをホスティングし、Androidプロジェクトから参照することができそうです。

この記事では、Android Libraryの作成、GitHub Packagesへの公開設定とGitHub Actionsを使用した自動アップロード、そして公開したライブラリのAndroidプロジェクトからの利用方法についてまとめます。

サンプルコード

https://github.com/horie1024/github-packages-android-sample

作業環境

Android Studioのバージョンは3.5.2を使用しています。

Android Libraryの作成

ライブラリを作成してみます。次のようなHelloクラスを作成しライブラリとして公開してみます。

Hello.kt
class Hello {
    companion object {
        fun world(name: String) = "Hello World ${name}!"
    }
}

使い方は次の通りです。

Hello.world(name = "Horie1024") // Hello World Horie1024!

プロジェクトの作成

Android Developersのプロジェクトの作成を参考にプロジェクトを作成します。

Android Studioのメインメニューから [File] > [New] > [New Project]を選択します。Create New Projectウィザードが表示されるので、「Empty Activity」を選択してNextをクリックします。

image.png

プロジェクトの設定をします。各項目の詳細は「プロジェクトを設定する」を参照してください。Finishをクリックするとプロジェクトが作成されます。

image.png

Libraryモジュールの作成

Libraryモジュールを作成します。ライブラリのコードは、Libraryモジュールに書いていきます。Android DevelopersのAndroid ライブラリの作成を参考にLibraryモジュールを作成します。

Android Studioのメインメニューから [File] > [New] > [New Module]を選択します。Create New Moduleウィザードが表示されるので、「Android Library」を選択してNextをクリックします。

image.png

ライブラリの名前、コードのMinimum SDKバージョンを指定して[Finish] をクリックします。

image.png

「mylibrary」というLibraryモジュールが作成されました。

image.png

appモジュールからの参照

ライブラリの開発を行う場合、appモジュールから参照できた方が便利です。Libraryモジュールを作成するとsettings.gradleが次のように更新されていることを確認します。

settings.gradle
include ':app', ':mylibrary'

appモジュールのbuild.gradledependenciesにブロックに次の行を追加します。

app/build.gradle
dependencies {
    implementation project(":mylibrary")
}

これでappモジュールからLibraryモジュールのコードを参照できるようになり、次のように使用できます。

MainActivity.kt
import com.horie1024.mylibrary.Hello

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        hello_world.text = Hello.world(name = "Horie1024")
    }
}

ビルドしてアプリを起動するとこんな感じです。

image.png

GitHub Packagesへの公開設定

作成したライブラリをGitHub Packagesへアップロードして公開します。公開するための設定はConfiguring Gradle for use with GitHub Packagesにまとめられています。必要になる設定は次の2つです。

  • 認証設定
  • ライブラリの公開設定

Maven Publish Pluginを使用して設定しますので、Libraryモジュールのbuild.gradleの先頭にapply 'maven-publish'を追加します。

mylibrary/build.gradle
apply 'maven-publish'

認証設定

GitHub Packagesでのライブラリの公開、ダウンロード、削除には認証が必要になり、GitHubのユーザ名とAccess Tokenが必要になります。

Libraryモジュールのbuild.gradleにpublishingブロックを定義し、そこに設定を追加します。urlはライブラリの公開先で、https://maven.pkg.github.com/OWNER/REPOSITORYの形で指定します。そして、credentialsusenameにGitHubのユーザー名、passwordにAccess Tokenを指定します。

mylibrary/build.gradle
publishing {
    repositories {
        maven {
            name = "GitHubPackages"
            url = uri("https://maven.pkg.github.com/horie1024/github-packages-android-sample")
            credentials {
                username = project.findProperty("gpr.user") ?: System.getenv("USERNAME")
                password = project.findProperty("gpr.token") ?: System.getenv("TOKEN")
            }
        }
    }
}

ライブラリの公開設定

GitHub Packagesがサポートするpackageのフォーマット(pom.xml)にライブラリのソースコードを変換する必要があります。

publishingpublicationsブロックを定義し、ライブラリの公開設定を追加します。ここで①の設定を書かない場合、ライブラリの推移的依存関係が含まれなくなります。

mylibrary/build.gradle
apply 'maven-publish'

task sourceJar(type: Jar) {
    from android.sourceSets.main.java.srcDirs
    archiveClassifier.set("sources")
}

publishing {
    publications {
        maven(MavenPublication) {
            groupId "com.horie1024"
            artifactId "github-packages-sample-library"
            version "$VERSION"
            artifact sourceJar
            artifact "$buildDir/outputs/aar/mylibrary-release.aar"

            // include any transitive dependencies・・①
            pom {
                withXml {
                    def dependenciesNode = asNode().appendNode('dependencies')

                    project.configurations.implementation.allDependencies.each {
                        if (it.group != null || it.name != null || it.version != null || it.name == "unspecified") return

                        def dependencyNode = dependenciesNode.appendNode('dependency')
                        dependencyNode.appendNode('groupId', it.group)
                        dependencyNode.appendNode('artifactId', it.name)
                        dependencyNode.appendNode('version', it.version)
                    }
                }
            }
        }
    }
    repositories {
        maven {
            name = "GitHubPackages"
            url = uri("https://maven.pkg.github.com/horie1024/github-packages-android-sample")
            credentials {
                username = project.findProperty("gpr.user") ?: System.getenv("USERNAME")
                password = project.findProperty("gpr.token") ?: System.getenv("TOKEN")
            }
        }
    }
}

また、ライブラリの公開設定はwupdigital/android-maven-publishを使用するとより簡単に記述できます。ライブラリを使用する側から、ライブラリのKotlinのソースコードを追うことができなかったので今回は使用を見送っています。

手動での公開

Access TokenはGitHubの[Settings] > [Developer settings] > [Personal access tokens] から作成します。scopeはrepowrite:packagesread:packagesにチェックを付けて生成します。

image.png

ユーザー名とtokenをgradle.propertiesに次のように定義するか、環境変数として定義します。

gradle.properties
gpr.user = horie1024
gpr.token = 123456789abcdefghijklmnopqrstuvwxyz

次のコマンドを実行するとGitHub Packagesへライブラリが公開されます。

./gradlew assembleRelease publish

そして、公開が成功するとRepositoryのpackagesページに次のようなページが追加されます。これで作成したライブラリを公開できました。

image.png

GitHub Actionsの設定

手動での公開ができたのでGitHub Actionsで自動化しましょう。

今回はmasterブランチにコミットがpushされたタイミングでGitHub Packagesにライブラリが公開されるようにします。これは次のymlをプロジェクトトップの.github/workflows以下に配置することで実現します。

publish_library.yml
name: "Publish library to GitHub Packages"
on:
  push:
    branches:
      - master

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@master
    - name: Publish library
      env:
        USERNAME: horie1024
        TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: ./gradlew assembleRelease publish

triggerの設定

masterブランチにコミットがpushされたタイミングでワークフローがトリガーされて欲しいので、onで制御します。次のように設定することでmasterブランチにpushされた場合にワークフローをトリガーすることができます。

on:
  push:
    branches:
      - master

jobの設定

ワークフローはjob単位で実行されていきます。ここではpublish jobを定義します。

runs-onはjobを実行する環境でubuntu-latestを指定します。stepsはjobを構成する一連のタスクです。1つ目のstepではuses: actions/checkout@masterでソースコードをチェックアウトし、2つ目のPublish library stepでライブラリの公開を実行します。

Publish library stepでは、envでstep内でのみ有効な環境変数を定義しています。ここでTOKENにはsecrets.GITHUB_TOKENを指定します。GITHUB_TOKENはワークフロー内で自動的に作られる環境変数です(詳細はこちら)。また、GITHUB_TOKENにどのようなパーミッションが与えられているかはこちらから確認でき、GitHub Packagesについてもread/writeパーミッションを持っています。

そして、run./gradlew publishを実行することでライブラリが公開されます。

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@master
    - name: Publish library
      env:
        USERNAME: horie1024
        TOKEN: ${{ secrets.GITHUB_TOKEN }}
      run: ./gradlew assembleRelease publish

公開したライブラリの利用

GitHub Packagesで公開したライブリを利用するには、認証設定が必要です。現時点(2019/12/01)でpublicなrepositoryの場合でも認証が必要になります。

認証設定

プロジェクトトップのbuild.gradle内、allprojectsブロックのrepositoriesブロックにライブラリの公開設定と同様の設定を追加します。

build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    ext.kotlin_version = '1.3.60'
    repositories {
        google()
        jcenter()

    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.2'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()

        // repositoriesにGitHub Packagesを追加
        maven {
            name = "GitHubPackages"
            url = uri("https://maven.pkg.github.com/horie1024/github-packages-android-sample")
            credentials {
                username = USERNAME // GitHubのユーザ名
                password = ACCESS_TOKEN // 発行したAccess Tokenを指定
            }
        }
    }
}

Access Tokenは、read:packages scopeを指定して作成します。用途としてはライブラリをダウンロードするだけなのでscopeは絞った方が良いでしょう。

image.png

dependenciesへの追加

dependenciesブロックにライブラリを追加し、「Sync Project with Gradle files」を実行すると公開したライブラリを使用できるようになります。

app/build.gradle
android {
    // androidブロックの定義
}

dependencies {

    // 公開したライブラリをimplementationに追加
    implementation 'com.horie1024:github-packages-sample-library:1.0.0'
}

まとめ

Androidのライブラリ作成してGitHub Packagesで公開してみました。GitHub Actionsと組み合わせるとライブラリの自動公開まで簡単に実現でき、今後社内向けのAndroidライブラリはGitHub Packagesでホスティングしていこうと考えています。

明日は@kenz_firespeedさんによる「今風の画像アップローダーを作る」です。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away