Using GitHub Packages for Android projects

GitHub has released GitHub Packages last year. You can now easily host your private libraries without subscribing to another service.

In this post, we’re going to setup GitHub Packages to publish an example Android library.

We’ll be following these steps:

  1. Creating credentials for accessing GitHub’s maven repository
  2. Publishing an Android library to GitHub’s maven repository using a Gradle plugin
  3. Installing an Android library in another project

The sample project is available on GitHub here: https://github.com/rubensousa/AndroidGithubPackage

Setup credentials

The maven repository for GitHub Packages has the following pattern:

https://maven.pkg.github.com/{repository owner}/{repository}

You need to provide valid credentials to publish or install packages from this repository.

repositories {
   maven {
       url = "https://maven.pkg.github.com/githubuser/Library"
       credentials {
           username = GITHUB_USER
           password = GITHUB_TOKEN
       }
   }
}

To get a token, go to your Settings page on your GitHub account. From there, go to Developer Settings -> Personal access tokens and generate a new one with the following permissions:

read:packages
write:packages
repo

Check the official instructions here

Note: These credentials shouldn’t be added to your git repository. Instead, you should define GITHUB_USER and GITHUB_TOKEN in an untracked properties file or pass them directly using the gradlew -P parameter for the publish task.

Publishing packages

With the recent release of Android Studio 3.6, publishing to maven repositories got simpler. You can read more about the maven-publish plugin here. The Android gradle plugin will now generate a proper POM file based on the project’s dependencies.

For publishing maven packages, I use an adapted version of Chris Banes’ goodie gradle-mvn-push which you can find here: publish_github.gradle

This plugin does the following:

  1. Specifies the maven repository and its credentials
  2. Generates the javadocs from java/kotlin files (using dokka if it’s available and properly setup in the project)
  3. Bundles the aar, javadocs and sources to be published

Feel free to modify the plugin for your own needs. The instructions are almost the same as the ones over gradle-mvn-push. For each library of your project, add this to your gradle.properties:

LIBRARY_VERSION=1.0.0
LIBRARY_GROUP=com.library.package
LIBRARY_ARTIFACT=library
// Gradle path for installation will be: com.library.package:library:1.0.0

If you have multiple modules and they all share the same properties, you can place this in the root gradle.properties:

LIBRARY_PUBLISH_SOURCES=true // default is true
LIBRARY_PUBLISH_DOCS=true // default is true

// The user/organization that owns the repository
GITHUB_OWNER=githubuser
// The name of the repository that contains your module
GITHUB_REPOSITORY=Library
// Maven repository url will be: https://maven.pkg.github.com/githubuser/Library

// The user with permissions to publish to the repository
GITHUB_USER=githubuser
GITHUB_TOKEN=PlaceThisInYourHomeGradlePropertiesAndOrCiSecret

POM_PACKAGING=aar
POM_NAME=Library
POM_DESCRIPTION=A sample library that will be published to github packages
POM_URL=https://github.com/githubuser/Library
POM_SCM_URL=https://github.com/githubuser/Library
POM_SCM_CONNECTION=scm:git@github.com:githubuser/Library.git
POM_SCM_DEV_CONNECTION=scm:git@github.com:githubuser/Library.git
POM_DEVELOPER_ID=githubuser
POM_DEVELOPER_NAME=GitHub User
// Optional license fields. If a license name isn't available,
// the artifact won't contain any license
POM_LICENCE_NAME=The Apache Software License, Version 2.0
POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
POM_LICENCE_DIST=repo

If it’s a single module project, everything can be defined in the same file.

Finally, apply the plugin at the bottom of your module’s build.gradle:

// Use this if the script is stored locally in the /gradle folder
apply from: "$rootProject.projectDir/gradle/publish_github.gradle"

// Or use this if the script is stored remotely somewhere:
apply from: "https://gist.github.com/user/publish.gradle"

To publish the library you can now simply run the publish task:

./gradlew publish

If you’re running this task using a CI service, like GitHub Actions, you can pass the properties needed as parameters:

./gradlew publish -PGITHUB_USER=${ {secrets.GITHUB_USER} } -PGITHUB_TOKEN=${ {secrets.GITHUB_TOKEN} }

After the task has finished running, packages can be found at: https://github.com/githubuser/Library/packages

Note: GITHUB_USER is the user that has permissions to read/write to the repository while GITHUB_OWNER is the user/organization that owns the repository.

Installing packages

Now that we’ve published our module to GitHub Packages, it’s time to install it from another project. Update your root build.gradle file with the new repository:

repositories {
   maven {
       url = "https://maven.pkg.github.com/githubuser/Library"
       credentials {
           username = GITHUB_USER
           password = GITHUB_TOKEN
       }
   }
}

And as usual, include the library in the module’s build.gradle:

implementation com.library.package:library:1.0.0

Unfortunately, this must be done even for open-source projects. We can only hope GitHub releases support for a central repository so that the setup becomes something like:

repositories {
   maven {
       url = "https://maven.pkg.github.com/"
   }
}

However, for open-source projects you can still use jcenter or jitpack (they’re both free for open-source).

Installing multiple packages

If you have a group of libraries, adding them becomes cumbersome because there’s no support for a “central” repository like jcenter or mavenCentral. This will eventually be the end result:

repositories {
   maven {
       url = "https://maven.pkg.github.com/user/AndroidLibrary1"
       ...
   }
   maven {
       url = "https://maven.pkg.github.com/user/AndroidLibrary2"
       ...
   }
   maven {
       url = "https://maven.pkg.github.com/user/AndroidLibrary3"
       ...
   }
}

As you can see, this doesn’t look so good. For the moment, you can workaround this by publishing your libraries to another repository in GitHub. This repository’s purpose will be hosting the packages from the other repositories:

repositories {
   maven {
       url = "https://maven.pkg.github.com/user/AndroidLibraries"
       ...
   }
}

Note: At the moment, GitHub doesn’t allow duplicate package names for the same owner. If AndroidLibrary1 is published to /user/AndroidLibrary1, it can’t be published to /user/AndroidLibraries and vice-versa.

Conclusion

GitHub Packages are really helpful and play together nicely with GitHub’s ecosystem. You can easily create private android modules and make them available privately or publicly.

However, I found a few drawbacks:

  1. Boilerplate setup for open-source packages. Importing a library requires adding its maven repository, which means N libraries will require N entries in the build.gradle file. For open-source projects I still recommend jcenter or jitpack.
  2. Limited support for publishing to different repositories. A library that’s present in a repository can’t be added to another one if it has the same artifact id.

Don’t forget to check the sample project at: https://github.com/rubensousa/AndroidGithubPackage